package com.me.myFramework.myAop.aop110;

import com.me.myFramework.myAop.IBuy;
import com.me.myFramework.myAop.Switch;
import com.me.myFramework.myAop.aop110.intercept.method.AfterReturningAdviceInterceptor;
import com.me.myFramework.myAop.aop110.intercept.method.MethodBeforeAdviceInterceptor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 自己实现 AOP-110：  <p></p>
 *
 * 提取模板方法 MethodBeforeAdviceInterceptor
 *
 * @author ME
 */
public class MyAop110 {

    /**
     * 通过 模板模式 提取增强逻辑：方法执行前、正常返回、异常返回
     */
    public static void main(String[] args) throws Throwable {
        IBuy target = new Switch();

        IBuy proxy = (IBuy) new MyAop110().getProxy(target);
        proxy.buy("Jack");

        System.out.println("\n----------------------------------------------");
        proxy = (IBuy) new MyAop110().getProxy2(target);
        proxy.buy("Tom");
    }

    private Object getProxy(Object target) {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // TODO 抽取模板方法：不同类型的 advice（调用前、正常返回时） 在哪里切入是确定的（方法前、方法后），所以可以抽取成不同格式的模板方法
                MethodBeforeAdviceInterceptor interceptor = new MethodBeforeAdviceInterceptor() {
                    @Override
                    protected void before(Method method1, Object[] args1, Object target1) {
                        String methodName = method1.getName();
                        // 仅仅是不想对 Object方法执行切面逻辑
                        if (isCustomMethod(methodName)) {
                            System.out.println("[AOP] 就是想记录谁消费了：" + args1[0]);
                        }
                    }
                };

                return interceptor.invoke(method, args, target);
            }
        });
    }

    // TODO 注意看 invoke()，对于不同的 advice 存在不同的写法，所以还需要对方法实现做进一步封装，把所有个性方法都抽出去
    private Object getProxy2(Object target) {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                AfterReturningAdviceInterceptor interceptor = new AfterReturningAdviceInterceptor() {
                    @Override
                    protected void afterReturning(Object returnValue, Method method1, Object[] args1, Object target) {
                        if (isCustomMethod(method1.getName())) {
                            System.out.println("[AOP2] 喜提一台 " + returnValue);
                        }
                    }
                };

                return interceptor.invoke(method, args, target);
            }
        });
    }

    boolean isCustomMethod(String methodName) {
        return !"toString".equals(methodName) && !"hashCode".equals(methodName) && !"equals".equals(methodName)
                && !"clone".equals(methodName);
    }

}
